﻿using System;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.Serialization;
using System.Windows.Forms;
using SoftwareConsulting.BI11.Studio.BasicEntities;
using SoftwareConsulting.BI11.Studio.BasicEntities.Extensions;
using SoftwareConsulting.BI11.Studio.Core.Project;
using SoftwareConsulting.BI11.Studio.Core.Project.SituationsHierarchy;
using SoftwareConsulting.BI11.Studio.InternalServices;
using SoftwareConsulting.Interfaces.BasicEntities;
using SoftwareConsulting.BI11.Studio.UI.Properties;
using SoftwareConsulting.BI11.Studio.Core.Project.Metadata;
using SoftwareConsulting.BI11.NumericalMethods.Geometry;

namespace SoftwareConsulting.BI11.Studio.UI.ProjectExplorer.SituationsHierarchy
{    
    /// <summary>
    /// Класс прямоугольника ситуации в рабочем пространстве иерархии ситуаций
    /// </summary>
    [Serializable]
    public sealed class SituationBox : Named, IDisposable
    {
        #region внутренние переменные
        [NonSerialized]private bool _killing;//флаг удаления прямоугольника ситуации
        private int _X; //X-координата прямоугольника
        private int _Y; //Y-координата прямоугольника       
        private Guid _situationId = Guid.Empty; //идентификатор связанной с прямоугольником ситуации
        [NonSerialized]private TextBox _control; //контрол-прямоугольник        
        [NonSerialized]private bool _isLinkStart; //флаг, означающий что данная ситуация указана как стартовая для проведения связи
        [NonSerialized]private Nullable<Point> _movementSpan; //класс перемещения объекта-прямоугольника ситуации
        [NonSerialized]private bool _isNoBordersReachedByGroup; //флаг допущения передвижения группы        
        [NonSerialized]private bool _renamingProcess;//флаг отключения переименования
        [NonSerialized]private bool _isFlashedOnFocus;//флаг подсветки прямоугольника ситуации при выделении
        [NonSerialized]private bool _flashedOnHover;//флаг подсветки прямоугольника ситуации при наведении мышки
        [NonSerialized]private Panel _hoverFlashSituation; //рамка для подсветки
        [NonSerialized]private Timer _hoverTimer; //таймер отсчитывающий продолжительность нахождения курсора мышки на прямоугольнике ситуации
        [NonSerialized]private bool _tabbingProcess = false; //флаг обработки табуляции        
        [NonSerialized]private ProjectDescriptor _projectLink; //для связи с проектом
        [NonSerialized]private ProjectExplorerForm _projectExplorerForm;
        [NonSerialized]private SituationsArea _area;
        #endregion

        #region публичные свойства
        /// <summary>
        /// Возвращает/устанавливает связь с родительской областью ситуаций
        /// </summary>
        public override IUnique Parent
        {
            get
            {
                return base.Parent;
            }
            set
            {
                SituationsArea area = value as SituationsArea;
                if (value != null) //null при вызове базового конструктора
                {
                    Assert.FailIf(area == null, Assert.Id.Assert00205);
                }
                base.Parent = value;
                _area = area;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает флаг подсветки прямоугольника ситуации при выделении
        /// </summary>        
        public bool IsFlashedOnFocus
        {
            get
            {
                return _isFlashedOnFocus;
            }
            set
            {
                _isFlashedOnFocus = value;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает связь с проектом
        /// </summary>        
        public ProjectDescriptor ProjectLink
        {
            get
            {
                return _projectLink;
            }
            set
            {
                _projectLink = value;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает связь с формой обозревателя проекта
        /// </summary>        
        public ProjectExplorerForm ProjectExplorerLink
        {
            get
            {
                return _projectExplorerForm;
            }
            set
            {
                _projectExplorerForm = value;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает флаг начальной ситуации связи
        /// </summary>        
        public bool IsLinkStart
        {
            get
            {
                return _isLinkStart;
            }
            set
            {
                _isLinkStart = value;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает X-координату
        /// </summary>        
        public int X
        {
            get
            {
                return _X;
            }
            set
            {
                bool scalingProcess = (Parent != null) && _area.Scaling;
                if ((value >= 0) && 
                    ((Parent == null) 
                    || (_control == null) 
                    || scalingProcess 
                    || (value <= (_area.SituationsAreaSize.Width - _control.Width - 1)))
                    )
                {
                    int oldvalue = _X;
                    _X = value;
                    if ((_control != null) && !ProjectDescriptor.StateOfPreparation.HasFlag(PreparationStates.Serialization))
                        _control.Left = _X;                    
                    if ((_projectLink != null) && (_X != oldvalue) && !scalingProcess)
                        _projectLink.Modified = true;                    
                }
            }
        }
        
        /// <summary>
        /// Возвращает/устанавливает Y-координату
        /// </summary>        
        public int Y
        {
            get
            {
                return _Y;
            }
            set
            {
                bool scalingProcess = (Parent != null) && _area.Scaling;
                if ((value >= 0) && 
                    ((Parent == null) 
                    || (_control == null) 
                    || scalingProcess 
                    || (value <= (_area.SituationsAreaSize.Height - _control.Height - 1)))
                    )
                {
                    int oldvalue = _Y;
                    _Y = value;
                    if ((_control != null) && !ProjectDescriptor.StateOfPreparation.HasFlag(PreparationStates.Serialization))
                        _control.Top = _Y;                    
                    if ((_projectLink != null) && (_Y != oldvalue) && !scalingProcess)
                        _projectLink.Modified = true;                    
                }
            }
        }

        /// <summary>
        /// Возвращает/устанавливает идентификатор ситуации
        /// </summary>        
        public Guid SituationId
        {
            get
            {
                return _situationId;
            }
            set
            {
                _situationId = value;
            }
        }

        /// <summary>
        /// Возвращает контрол-прямоугольник
        /// </summary>        
        public TextBox Control
        {
            get
            {
                return _control;
            }
        }
        #endregion

        #region внутренние методы        
        private void Dispose(bool fromFinalizer)
        {
            if (_hoverTimer != null)
            {
                _hoverTimer.Stop();
                _hoverTimer.Dispose();
            }
            if (_hoverFlashSituation != null)
                _hoverFlashSituation.Dispose();            
            if (_control != null)
                _control.Dispose();            
            if (fromFinalizer)
                this.NoDisposeWarning();            
        }

        /// <summary>
        /// Инициализирует класс
        /// </summary>        
        private void InitializeClass()
        {
            _killing = false;
            _isLinkStart = false;
            _isNoBordersReachedByGroup = true;
            _renamingProcess = false;
            _isFlashedOnFocus = false;
            _flashedOnHover = false;
            _hoverTimer = null;
        }

        [OnDeserializedAttribute()]
        private void WhenDeserialized(StreamingContext context)
        {
            InitializeClass();
        }

        /// <summary>
        /// Отображает подсветку вокруг прямоугольника ситуации
        /// </summary>
        private void CreateHoverFlashSituation(Color color)
        {
            if (_hoverFlashSituation == null)
            {                
                if (_projectExplorerForm.CurrentSettings.PlaySounds)
                {
                    System.Media.SoundPlayer MySounds = _projectExplorerForm.SoundsPlayer;
                    MySounds.Play();
                }
                _hoverFlashSituation = new Panel();
                _hoverFlashSituation.Visible = false;
                _hoverFlashSituation.Parent = _control.Parent;
                _hoverFlashSituation.BorderStyle = BorderStyle.None;
                _hoverFlashSituation.BackColor = color;
                _hoverFlashSituation.Left = X - 2;
                _hoverFlashSituation.Top = Y - 2;
                _hoverFlashSituation.Height = _control.Height + 4;
                _hoverFlashSituation.Width = _control.Width + 4;
                _hoverFlashSituation.Visible = true;
            }
        }

        /// <summary>
        /// Обработка потери фокуса прямоугольником ситуации
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Leave(object sender, EventArgs e)
        {
            if (_projectLink.Active 
                && !ProjectDescriptor.StateOfPreparation.HasFlag(PreparationStates.Deserialization))
            {
                TryRename();
            }
        }

        /// <summary>
        /// Обработка приобретения фокуса прямоугольником ситуации
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Enter(object sender, EventArgs e)
        {            
            if ((_area.State != SituationsHierarchyState.Pointer) 
                && (_area.State != SituationsHierarchyState.NewSituation) 
                && (_area.MultiselectedSituationBoxes == null))
            {
                //if ((dsform.scrollPositionToFixBugWithDoubleEnteredFrameBox == null)&&(_owningDesignSpace.selectedFrameBox!=null))
                //{
                  //  dsform.scrollPositionToFixBugWithDoubleEnteredFrameBox = new Point(-1, -1);
                //}
                //УБРАЛ ИЗ-ЗА БАГИ:
                //ЕСЛИ СФОКУСИРОВАНА СИТУАЦИЯ SITUATION1
                //И МЫ ПЫТАЕСЯ КЛИКНУТЬ ЛИНКОЙ НА SITUATION2
                //ТО РП АВТОМАТОМ ОТСКРОЛИРУЕТСЯ ВНАЧАЛО
                //И ЭТО ВООБЩЕ НИКАК НЕ ОБОЙТИ
                //ПОХОЖЕ СОБАКА ЗАРЫТА В ТОМ, ЧТО ДВАЖДЫ
                //ВЫЗЫВАЕТСЯ ONENTER У SITUATION2, НО КАК ЭТО
                //ОБОЙТИ НЕ ЗНАЮ
                //ДЕЛАЛ ДАЖЕ СПЕЦИАЛЬНУЮ ЗАТЫЧКУ НО НЕ ПОМОГЛО
                //shform.FocusSituationsArea();                                 
            }            
            _control.Tag = _control.Text;            
        }

        /// <summary>
        /// Обработка покидания курсором мышки области прямоугольника ситуации
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MouseLeave(object sender, EventArgs e)
        {            
            if (_hoverTimer!=null)
            {
                _hoverTimer.Stop();
                _hoverTimer = null;
                if (_area.HoveredNonselectedSituationBox != null)
                {
                    SituationsHierarchyForm shform = _area.SituationsHierarchyForm;
                    SituationBox sitbox = _area.SelectedSituationBox;
                    Situation sit = sitbox == null ? null : _projectLink.GetItemById(sitbox.SituationId);
                    shform.UpdateCharacteristicsGrid(sit);
                    //здесь не надо shform.RefreshDS, потому что 
                    //почему-то перерисовка области ситуаций автоматом 
                    //вызывается далее в UpdateView..
                    //наверное во время Dispose'а рамки ситуации
                }
            }
            _flashedOnHover = false;
            UpdateView();
        }

        /// <summary>
        /// Обработка тика таймера отсчета продолжительности нахождения курсора мышки над прямоугольником ситуации
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Tick(object sender, EventArgs e)
        {            
            _hoverTimer.Stop();
            if (Parent != null) //бывает при удалении ситуации
            {
                SituationBox sitbox = _area.SelectedSituationBox;
                if ((sitbox == null) || (sitbox != this))
                {
                    SituationsHierarchyForm shform = _area.SituationsHierarchyForm;
                    shform.UpdateCharacteristicsGrid(_projectLink.GetItemById(_situationId));
                    shform.RefreshSituationsArea();
                }
            }
        }

        /// <summary>
        /// Обработка входа курсора мышки в область прямоугольника ситуации
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MouseEnter(object sender, EventArgs e)
        {
            _flashedOnHover = true;
            IHierarchy<SituationBox> msfb = _area.MultiselectedSituationBoxes;
            if ((msfb == null) || !msfb.ContainsId(Id))
            {
                _hoverTimer = new Timer();
                _hoverTimer.Interval = 1000;
                _hoverTimer.Tick += new EventHandler(Tick);
                _hoverTimer.Enabled = true;
                _hoverTimer.Start();
            }
            UpdateView();
        }

        /// <summary>
        /// Обработка отпускания кнопки клавиатуры при редактировании имени ситуации
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyData.ToString() == "Delete") //только так почемуто проверяется Del
            {
                _area.SituationsHierarchyForm.DeleteSelectedSituations();
                return;
            }            
            if (e.KeyCode == Keys.ControlKey)
            {
                _area.IsMultiselectByCtrl = false;
            }                                    
        }

        /// <summary>
        /// Обработка нажатия кнопки клавиатуры при редактировании имени ситуации
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void KeyPress(object sender, KeyPressEventArgs e)
        {
            if (_tabbingProcess)
            {
                e.Handled = true;
                _tabbingProcess = false;
                return;
            }            
            if (e.KeyChar == '\u001B')
            {
                //эксейп нажали, его както глючно обрабатывает (будто это символ стрелочка)
                return;
            }
            string s = _control.Tag as string;
            int ind = _control.SelectionStart;
            int len = _control.SelectionLength;
            if (e.KeyChar != '\b')
            {
                //вставили букву
                if (ind <= s.Length)
                {
                    //не в конец
                    if (len == 0)
                    {
                        //просто одну букву
                        s = s.Substring(0, ind) + e.KeyChar + s.Substring(ind);
                    }
                    else
                    {
                        //было выделение, 
                        //значит это выделение надо заменить на новую букву
                        s = s.Substring(0, ind) + e.KeyChar + s.Substring(ind + len);
                    }
                }
                else
                {
                    //в конец
                    s = s + e.KeyChar;
                }
            }
            else
            {
                //удалили букву
                if (ind > 0)
                {
                    //было что удалять
                    if (ind <= s.Length)
                    {
                        //не последнюю
                        if (len == 0)
                        {
                            //одну букву
                            s = s.Substring(0, ind - 1) + s.Substring(ind + len);
                        }
                        else
                        {
                            //несколько букв
                            s = s.Substring(0, ind) + s.Substring(ind + len);
                        }
                    }
                    else
                    {
                        //последнюю
                        s = s.Substring(0, s.Length - 1);                            
                    }
                }
            }
            _control.Tag = s;                
            s = s.Trim();
            ErrorProvider ep = (_area.SituationsHierarchyForm).SituationIncorrectProvider;
            Situation sit = _projectLink.GetItemById(_situationId);
            string canttrimError = "";
            try
            {
                s = GeneralMethods.TrimName(s);
            }
            catch (BIIncorrectNameException ex)
            {
                canttrimError = ex.Message;              
            }
            if (!string.IsNullOrEmpty(canttrimError) || !_projectLink.CheckItemsForDuplicateByName(sit, s))
            {
                ep.SetError(_control, canttrimError);
            }
            else
            {
                ep.SetError(_control, "");
            }                        
        }

        /// <summary>
        /// Предварительная обработка вдавливания кнопки клавиатуры при редактировании имени ситуации
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {            
            if (e.KeyCode == Keys.Tab)
                e.IsInputKey = true;            
        }

        /// <summary>
        /// Обработка вдавливания кнопки клавиатуры при редактировании имени ситуации
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void KeyDown(object sender, KeyEventArgs e)
        {                        
            switch (e.KeyCode)
            {
                case Keys.Escape:
                    if (_area.IsLinkCreatingProcess)
                        _area.State = _area.State;                    
                    break;
                case Keys.Enter:
                    if (TryRename())
                    {
                        _renamingProcess = true;
                        _area.SituationsHierarchyForm.FocusSituationsArea();
                        _area.SituationsHierarchyForm.UpdateCharacteristicsGrid(_projectLink.GetItemById(_situationId));
                        _renamingProcess = false;
                    }
                    break;
                case Keys.ControlKey:
                    _area.IsMultiselectByCtrl = true;
                    break;
                case Keys.Down:                            
                    Y = Y + 8;
                    _area.ActualizeScheme(_area.MultiselectedSituationBoxes, true);
                    break;
                case Keys.Up:
                    Y = Y - 8;
                    _area.ActualizeScheme(_area.MultiselectedSituationBoxes, true);
                    break;
                case Keys.Left:
                    X = X - 8;
                    _area.ActualizeScheme(_area.MultiselectedSituationBoxes, true);
                    break;
                case Keys.Right:
                    X = X + 8;
                    _area.ActualizeScheme(_area.MultiselectedSituationBoxes, true);
                    break;
                case Keys.Tab:
                    SituationBox gfrbox = _area.GetNextSituationBoxByTabOrder(e.Shift ? TaborderDirection.Backward : TaborderDirection.Forward);
                    if (gfrbox != null)
                    {
                        Situation gsit = _projectLink.GetItemById(gfrbox.SituationId);
                        _area.GotoSituationBoxBySituation(gsit);                                               
                    }
                    _tabbingProcess = true;
                    break;
                default:                    
                    _control.Size = _control.PreferredSize;
                    if (_hoverFlashSituation != null)
                    {
                        _hoverFlashSituation.Width = _control.Width + 4;
                        _control.Height = _control.Height + 4;
                    }                    
                    break;
            }
        }

        /// <summary>
        /// Обработка двойного щелчка мышкой по прямоугольнику ситуации
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MouseDoubleClick(object sender, MouseEventArgs e)
        {
            _area.SituationsHierarchyForm.ShowEditEventDialog();
        }

        /// <summary>
        /// Обработка щелчка мышкой по прямоугольнику ситуации
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MouseClick(object sender, MouseEventArgs e)
        {
            if ((_area.MultiselectedSituationBoxes == null) || (!_area.MultiselectedSituationBoxes.Contains(this)) && (!_area.IsMultiselectByCtrl))
            {
                SituationsHierarchyForm shform = _area.SituationsHierarchyForm;
                SituationsHierarchyState sh_state = _area.State;
                if ((sh_state == SituationsHierarchyState.IsA) 
                    || (sh_state == SituationsHierarchyState.Subsituation) 
                    || (sh_state == SituationsHierarchyState.NextStep) || 
                    (sh_state == SituationsHierarchyState.StepOnFail))
                {
                    SituationBox sitbox = _area.GetStartLinkPoint();                    
                    if (sitbox == null)
                    {
                        _isLinkStart = true;
                        _area.IsLinkCreatingProcess = true;
                        _area.SelectedLinkLine = null;
                        shform.SituationsAreaTip = Resources.BI_SITUATIONSHIERARCHY_TOOLTIP_SELECT_FINISH;
                    }
                    else
                    {
                        sitbox.IsLinkStart = false;
                        _area.IsLinkCreatingProcess = false;
                        Situation sitfrom = _projectLink.GetItemById(sitbox.SituationId);
                        Situation sitto = _projectLink.GetItemById(_situationId);
                        try
                        {
                            switch (sh_state)
                            {
                                case SituationsHierarchyState.IsA:
                                    sitfrom.SetIsA(sitto);
                                    break;
                                case SituationsHierarchyState.Subsituation:
                                    {
                                        Characteristic ch = new Characteristic(Characteristic.DefaultName, ProjectLink.GetSpecialIndicator(IndicatorType.Subsituation, true), sitto, sitfrom, true, null, ProjectLink, false, false);                                        
                                        sitfrom.Add(ch);
                                        sitfrom.UpdateInheritedCharacteristicsOfChilds(ch.Id, ch.Name, InheritedCharacteristicParts.All, false);
                                        break;
                                    }
                                case SituationsHierarchyState.NextStep:
                                    {
                                        Characteristic ch = new Characteristic(Characteristic.DefaultName, ProjectLink.GetSpecialIndicator(IndicatorType.NextStep, true), sitto, sitfrom, true, null, ProjectLink, false, false);
                                        sitfrom.Add(ch);
                                        //не обновляем дочерние, т.к. указатели на следующий шаг не наследуются
                                        break;
                                    }
                                case SituationsHierarchyState.StepOnFail:
                                    {
                                        Characteristic ch = new Characteristic(Characteristic.DefaultName, ProjectLink.GetSpecialIndicator(IndicatorType.StepOnFail, true), sitto, sitfrom, true, null, ProjectLink, false, false);                                        
                                        sitfrom.Add(ch);
                                        //не обновляем дочерние, т.к. указатели на шаг при неудаче не наследуются
                                        break;
                                    }
                            }                                                                                        
                        }
                        catch (BISituationsHierarchyException ex)
                        {
                            GeneralMethods.ShowUserError(ex.Message);                            
                        }                        
                        shform.RefreshSituationsArea();
                        shform.SituationsAreaTip = Resources.BI_SITUATIONSHIERARCHY_TOOLTIP_SELECT_START;
                    }
                }
                SituationBox situationbox = _area.SelectedSituationBox;
                Situation sit = situationbox == null ? null : _projectLink.GetItemById(situationbox.SituationId);
                _area.MultiselectedSituationBoxes = null;
                shform.UpdateCharacteristicsGrid(sit);
                IHierarchy<SituationBox> sitboxes = new Hierarchy<SituationBox>();
                sitboxes.Add(this);
                _area.ActualizeScheme(sitboxes, false);
            }            
        }

        /// <summary>
        /// Обработка вдавливания клавиши мыши на прямоугольнике ситуации
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MouseDown(object sender, MouseEventArgs e)
        {
            if (_area.State == SituationsHierarchyState.Pointer)
            {
                if (_area.IsMultiselectByCtrl)
                {
                    if (_area.MultiselectedSituationBoxes == null)
                    {
                        _area.MultiselectedSituationBoxes = new Hierarchy<SituationBox>();
                    }
                    SituationBox curfb = _area.SelectedSituationBox;
                    IHierarchy<SituationBox> msfb = _area.MultiselectedSituationBoxes;
                    if (curfb != null)
                    {
                        if (!msfb.Contains(curfb))
                            msfb.Add(curfb);                        
                        _area.SelectedSituationBox = null;
                    }
                    if (!msfb.Contains(this))
                    {
                        msfb.Add(this);
                    }
                    else
                    {
                        msfb.DeleteById(Id);
                    }
                    SituationsHierarchyForm shform = _area.SituationsHierarchyForm;
                    shform.UpdateCharacteristicsGrid(null);
                    _area.ActualizeScheme(msfb, false);
                    shform.RefreshSelectedSituationActionsAbilities();
                }
                else
                {
                    Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;
                    _movementSpan = new Point(e.X,e.Y);
                    _area.SelectedSituationBox = this;
                    IHierarchy<SituationBox> sitboxes = new Hierarchy<SituationBox>();
                    sitboxes.Add(this);
                    _isNoBordersReachedByGroup = true;
                    _area.ActualizeScheme(sitboxes, true);
                }
            }
            else
            {
                if (e.Button == MouseButtons.Left)
                {
                    _area.SelectedSituationBox = null;
                }
                else
                {
                    //для того, чтобы не падало при вызове контекстного меню
                    //с курсором не равным Pointer'у
                    _area.SelectedSituationBox = this;
                }
                _area.SelectedLinkLine = null;
            }            
        }

        /// <summary>
        /// Обработка отпускания клавиши мыши на прямоугольнике ситуации
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MouseUp(object sender, MouseEventArgs e)
        {            
            _movementSpan = null;
            Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.Normal;            
        }

        /// <summary>
        /// Обработка движения курсора мыши над прямоугольником ситуации
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MouseMove(object sender, MouseEventArgs e)
        {            
            if (_movementSpan.HasValue && _isNoBordersReachedByGroup)
            {
                _isNoBordersReachedByGroup = _area.SituationsHierarchyForm.MoveSituationBox(this, e.X - _movementSpan.Value.X, e.Y - _movementSpan.Value.Y, false);
                if (_hoverFlashSituation != null)
                {
                    _hoverFlashSituation.Left = _control.Left - 2;
                    _hoverFlashSituation.Top = _control.Top - 2;
                }
            }
            else
            {
                TextBox tb = sender as TextBox;                
                if (tb.Cursor == Cursors.Hand)
                    tb.Cursor = Cursors.Default;                
            }            
        }
        #endregion

        #region публичные методы
        /// <summary>
        /// Изменение видимого размера прямоугольника ситуации
        /// </summary>
        /// <param name="scale"></param>
        public void ResizeRectangle(SituationsHierarchyScale scale)
        {
            int fontsize = 8;
            switch (scale)
            {
                case SituationsHierarchyScale.Less:
                    fontsize = 6;
                    break;
                case SituationsHierarchyScale.Little:
                    fontsize = 5;
                    break;
                case SituationsHierarchyScale.VeryLittle:
                    fontsize = 4;
                    break;
                case SituationsHierarchyScale.Panorama:
                    fontsize = 1;
                    break;
            }
            _control.Font = new Font(_control.Font.FontFamily, fontsize);
            _control.Size = _control.PreferredSize;
            if (_hoverFlashSituation != null)
            {
                _hoverFlashSituation.Width = _control.Width + 4;
                _hoverFlashSituation.Height = _control.Height + 4;
            }
        }

        /// <summary>
        /// Возвращает ближайший к заданной точке угол прямоугольника ситуации
        /// </summary>
        /// <param name="fromPoint">исходная точка</param>
        /// <param name="offset">смещение "шариков" на концах линий связи</param>
        /// <param name="reversed">false - ищем ближайший угол, true - ищем дальний угол</param>
        /// <param name="corner">найденный угол</param>
        /// <returns>точка окна РП</returns>
        public Point GetNearestCorner(Point fromPoint, int offset, bool reversed, out Corners corner)
        {
            Point p = new Point(X - offset, Y - offset); //изначально левый верхний угол            
            corner = Corners.TopLeft;
            if (_control != null)
            {
                int width = _control.Width;
                int height = _control.Height;
                Point[] points = new Point[4];
                points[0] = new Point(X,Y);
                points[1] = new Point(X + width, Y);
                points[2] = new Point(X + width, Y + height);
                points[3] = new Point(X, Y + height);
                double min = GeometricMethods.CalculateDistance(fromPoint, points[0]);
                int k = 0;
                for (int i = 1; i < 4; i++)
                {
                    double curdist = GeometricMethods.CalculateDistance(fromPoint, points[i]);
                    if (reversed != (curdist<min))
                    {
                        min = curdist;
                        k = i;
                    }
                }
                p = points[k];
                if ((k == 0) || (k == 3))
                {
                    p.X = p.X - offset;
                }
                else
                {
                    p.X = p.X + offset;
                }
                if ((k == 0) || (k == 1))
                {
                    p.Y = p.Y - offset;
                }
                else
                {
                    p.Y = p.Y + offset;
                }
                switch(k)
                {
                    case 0:
                        corner = Corners.TopLeft;
                        break;
                    case 1:
                        corner = Corners.TopRight;
                        break;
                    case 2:
                        corner = Corners.BottomRight;
                        break;
                    case 3:
                        corner = Corners.BottomLeft;
                        break;
                }                
            }            
            return p;
        }

        /// <summary>
        /// Применяет масштаб к прямоугольнику
        /// </summary>
        /// <param name="newScale"></param>
        public void ApplyScale(SituationsHierarchyScale newScale)
        {
            if (_area.ScalingFactorForRectangleX == -1)
            {
                int oldw = _control.PreferredSize.Width;
                int oldh = _control.PreferredSize.Height;
                ResizeRectangle(newScale);                
                int neww = _control.PreferredSize.Width;
                int newh = _control.PreferredSize.Height;
                _area.ScalingFactorForRectangleX = (float)neww / (float)oldw;
                _area.ScalingFactorForRectangleY = (float)newh / (float)oldh;
                _area.SavingScaleSituationBox = this;
            }
            else
            {
                if (!ProjectDescriptor.StateOfPreparation.HasFlag(PreparationStates.Serialization)) //во время сохранения нечего мигать компонентами когда делаем scale=NORMAL
                    ResizeRectangle(newScale);                
            }
            int newx = Convert.ToInt32(Math.Round(X * _area.ScalingFactorForRectangleX));
            int newy = Convert.ToInt32(Math.Round(Y * _area.ScalingFactorForRectangleY));
            if (!_area.Scaling)
            {
                _area.Scaling = true;
                X = newx;
                Y = newy;
                _area.Scaling = false;
            }
            else
            {
                X = newx;
                Y = newy;
            }
            if (!ProjectDescriptor.StateOfPreparation.HasFlag(PreparationStates.Serialization))
                _area.SituationsHierarchyForm.UpdateSituationsAreaSize();            
        }

        /// <summary>
        /// Меняет цвет прямоугольника ситуации в соответствии с текущей цветовой схемой
        /// </summary>        
        public void UpdateView()
        {            
            switch (_projectExplorerForm.Scheme)
            {
                case ColorScheme.System:
                    if ((_area == null) ||
                        ((this != _area.SelectedSituationBox) && ((_area.MultiselectedSituationBoxes == null) || (!_area.MultiselectedSituationBoxes.Contains(this)))))
                    {
                        _isFlashedOnFocus = false;
                        if (!_flashedOnHover)
                        {
                            if (_control.BackColor != SystemColors.InactiveCaption)
                            {
                                _control.BackColor = SystemColors.InactiveCaption;
                                _control.ForeColor = SystemColors.InactiveCaptionText;
                            }
                            if (_hoverFlashSituation != null)
                            {
                                _hoverFlashSituation.Dispose();
                                _hoverFlashSituation = null;
                            }
                        }
                        else
                        {
                            if (_control.BackColor != SystemColors.ActiveCaption)
                            {
                                _control.BackColor = SystemColors.ActiveCaption;
                                _control.ForeColor = SystemColors.ActiveCaptionText;
                            }
                            if (_hoverFlashSituation == null)
                            {
                                CreateHoverFlashSituation(SystemColors.Highlight);
                            }
                        }
                    }
                    else
                    {
                        IsFlashedOnFocus = true;
                        if (_control.BackColor != SystemColors.MenuHighlight)
                        {
                            _control.BackColor = SystemColors.MenuHighlight;
                            _control.ForeColor = SystemColors.MenuText;
                        }
                        if (_hoverFlashSituation != null)
                        {
                            _hoverFlashSituation.Dispose();
                            _hoverFlashSituation = null;
                        }
                    }
                    break;
                case ColorScheme.Red:
                    if ((_area == null) ||
                        ((this != _area.SelectedSituationBox) && ((_area.MultiselectedSituationBoxes == null) || (!_area.MultiselectedSituationBoxes.Contains(this)))))
                    {
                        IsFlashedOnFocus = false;
                        if (!_flashedOnHover)
                        {
                            if (_control.BackColor != System.Drawing.Color.Red)
                            {
                                _control.BackColor = System.Drawing.Color.Red;
                                _control.ForeColor = System.Drawing.Color.White;
                            }
                            if (_hoverFlashSituation != null)
                            {
                                _hoverFlashSituation.Dispose();
                                _hoverFlashSituation = null;
                            }
                        }
                        else
                        {
                            if (_control.BackColor != System.Drawing.Color.RosyBrown)
                            {
                                _control.BackColor = System.Drawing.Color.RosyBrown;
                                _control.ForeColor = System.Drawing.Color.RoyalBlue;
                            }
                            if (_hoverFlashSituation == null)
                            {
                                CreateHoverFlashSituation(System.Drawing.Color.Pink);
                            }
                        }
                    }
                    else
                    {
                        IsFlashedOnFocus = true;
                        if (_control.BackColor != System.Drawing.Color.Pink)
                        {
                            _control.BackColor = System.Drawing.Color.Pink;
                            _control.ForeColor = System.Drawing.Color.Blue;
                        }
                        if (_hoverFlashSituation != null)
                        {
                            _hoverFlashSituation.Dispose();
                            _hoverFlashSituation = null;
                        }
                    }
                    break;            
            }
            Situation sit = _projectLink.GetItemById(_situationId);
            if ((sit != null) && sit.ExcludedFromInference)
            {
                _control.BackColor = SystemColors.Window;
                _control.ForeColor = SystemColors.WindowText;
            }            
        }

        /// <summary>
        /// Попытка переименовать ситуацию
        /// </summary>
        /// <returns>true - удачно, false - неудачно</returns>
        public bool TryRename()
        {
            if (!_renamingProcess)
            {              
                Situation sit = _projectLink.GetItemById(_situationId);
                try
                {
                    if (!_killing) //killing когда ситуацию, только создав, сразу удалили (не выйдя из режима редактирования имени)
                        sit.SetName(_control.Text);                    
                }
                catch (BIIncorrectNameException ex)
                {
                    GeneralMethods.ShowUserError(ex.Message + " (" + _control.Text + ")");
                    _control.Focus();
                    _control.SelectAll();
                    return false;
                }
                catch (BISituationsHierarchySituationIncorrectNameException ex)
                {
                    GeneralMethods.ShowUserError(ex.Message + " (" + _control.Text + ")");
                    _control.Focus();
                    _control.SelectAll();
                    return false;
                }
                SetName(sit.Name);
            }            
            return true;
        }        

        public void Dispose()
        {
            Dispose(false);
        }

        /// <summary>
        /// Создает объект прямоугольника ситуации
        /// </summary>
        public void CreateRectangle(bool isDeserializing)
        {
            Situation sit = _projectLink.GetItemById(_situationId);
            SituationsHierarchyForm shform = _area.SituationsHierarchyForm;
            _control = new System.Windows.Forms.TextBox();
            _control.Visible = false;
            _control.BorderStyle = BorderStyle.FixedSingle;
            _control.Text = sit.Name;
            _control.TextAlign = HorizontalAlignment.Center;
            _control.Location = new Point(_X, _Y);            
            _control.Cursor = Cursors.Default;
            _control.PreviewKeyDown += new PreviewKeyDownEventHandler(PreviewKeyDown);
            _control.KeyDown += new KeyEventHandler(KeyDown);
            _control.KeyUp += new KeyEventHandler(KeyUp);
            _control.KeyPress += new KeyPressEventHandler(KeyPress);
            _control.Leave += new EventHandler(Leave);
            _control.Enter += new EventHandler(Enter);
            _control.ContextMenuStrip = shform.GetSituationBoxContext;           
            _control.MouseClick += new MouseEventHandler(MouseClick);
            _control.MouseDoubleClick += new MouseEventHandler(MouseDoubleClick);
            _control.MouseDown += new MouseEventHandler(MouseDown);
            _control.MouseUp += new MouseEventHandler(MouseUp);
            _control.MouseMove += new MouseEventHandler(MouseMove);
            _control.MouseEnter += new EventHandler(MouseEnter);
            _control.MouseLeave += new EventHandler(MouseLeave);            
            _control.Visible = true;
            _area.SelectedSituationBox = this;
            if (!isDeserializing)
                shform.UpdateCharacteristicsGrid(sit);
            ResizeRectangle(_area.Scale);            
        }

        /// <summary>
        /// Уничтожение всех остатков прямоугольника ситуации
        /// </summary>
        public void Kill(bool toRefreshDS)
        {
            _killing = true;
            if (_hoverFlashSituation != null)
            {
                _hoverFlashSituation.Dispose();
                _hoverFlashSituation = null;
            }
            _control.Dispose();
            if (Parent != null)
            {
                _area.Delete(this);
                if (_area.SelectedSituationBox == this)
                    _area.SelectedSituationBox = null;                
                SituationsHierarchyForm dsform = _area.SituationsHierarchyForm;
                if ((dsform != null) && toRefreshDS)
                {
                    dsform.RefreshSituationsArea();
                }
            }                        
            Parent = null;            
        }
        #endregion                                                                                

        #region конструкторы и финализаторы
        /// <summary>
        /// Конструктор класса
        /// </summary>
        public SituationBox(int x, int y, Situation situation, SituationsArea newOwningSituationsArea)            
        {
            InitializeClass();
            Parent = newOwningSituationsArea;
            _projectLink = _area.ProjectLink;
            _projectExplorerForm = _area.ProjectExplorerLink;
            X = x;
            Y = y;            
            _situationId = situation.Id;            
            CreateRectangle(false);
            SetName(situation.Name);
        }

        /// <summary>
        /// Финализатор класса
        /// </summary>
        ~SituationBox()
        {
            Dispose(true);
        }
        #endregion        
    }
}
